package com.ludomancy.gamelib.entity;

import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Vector;
import com.ludomancy.gamelib.Randomizer;
import com.ludomancy.gamelib.WorldScreenProjection;

public class World
{
	private class ZOrderComparator implements Comparator
	{
		public int compare(Object arg0, Object arg1)
		{
			Entity e1 = (Entity) arg0;
			Entity e2 = (Entity) arg1;
			return e1.z - e2.z;
		}
	}
	public int width;
	public int height;
	private Vector entities = new Vector();
	
	public Entity selected = null;
	public Entity hovering = null;
	
	public World(int worldWidth, int worldHeight)
	{
		this.width = worldWidth;
		this.height = worldHeight;
	}
	
	public boolean isIn(int tx, int ty)
	{
		return tx >= 0 && tx < width && ty >= 0 && ty < height;
	}
	
	private void insertEntityAt(Entity e, int at)
	{
		entities.insertElementAt(e, at);
		e.parent = this;
		e.onAdittion();
	}
	
	public void push(Entity e)
	{
		insertEntityAt(e, 0);
	}
	
	public void add(Entity e)
	{
		entities.add(e);
		e.parent = this;
		e.onAdittion();
	}
	
	public void remove(Entity e)
	{
		e.onRemoval();
		if(e.parent == this)
		{
			e.parent = null;
		}
		entities.remove(e);
		if(selected != null && selected == e)
		{
			selected = null;
		}
	}
	
	public void update()
	{
		Vector toUpdate = (Vector) entities.clone();
		for (Iterator iterator = toUpdate.iterator(); iterator.hasNext();)
		{
			Entity e = (Entity) iterator.next();
			e.update();
		}
	}
	
	public Vector getEntities(EntityFilter filter)
	{
		Vector ret = new Vector();
		for (Iterator iterator = entities.iterator(); iterator.hasNext();)
		{
			Entity e = (Entity) iterator.next();
			if(filter.accepts(e))
			{
				ret.add(e);
			}
		}
		return ret;
	}
	
	public void moveToUnoccupiedRandomPlace(Rectangle subArea, Entity toAdd)
	{
		moveToUnoccupiedRandomPlace(subArea, toAdd, null);
	}
	
	public void moveToUnoccupiedRandomPlace(Rectangle subArea, Entity toAdd, EntityFilter filter)
	{
		Point position = null;
		// leave a 4% margin
		int marginX = (0*width)/100;
		int marginY = (0*height)/100;
		while(position == null)
		{
			// random position
			int radiusWidth = toAdd.getWidth();
			int radiusHeight = toAdd.getHeight();
			position = new Point(Randomizer.getRandom(radiusWidth/2+marginX, width-radiusWidth/2-marginX), Randomizer.getRandom(radiusHeight/2+marginY, height-radiusHeight/2-marginY));
			if(subArea != null && !subArea.contains(position.x, position.y))
			{
				position = null;
				continue;
			}
			// check that no other entity collides with it
			toAdd.pos.setLocation(position);
			Vector near = getEntities(new EntityCollisionFilter(toAdd));
			for (Iterator iterator = near.iterator(); iterator.hasNext();)
			{
				Entity e = (Entity) iterator.next();
				if(filter != null)
				{
					if(filter.accepts(e))
					{
						continue;
					}
				}
				position = null;
				break;
			}
		}
	}
	
	public Entity getEntityAt(Point pos)
	{
		Entity sel = null;
		Vector toUpdate = (Vector) entities.clone();
		for (Iterator iterator = toUpdate.iterator(); iterator.hasNext();)
		{
			Entity e = (Entity) iterator.next();
			if(e.active() && e.area().contains(pos.x, pos.y))
			{
				sel = e;
			}
		}
		return sel;
	}
	
	public void paint(Graphics g, WorldScreenProjection projector)
	{
		Vector toUpdate = (Vector) entities.clone();
		Collections.sort(toUpdate, new ZOrderComparator());
		for (Iterator iterator = toUpdate.iterator(); iterator.hasNext();)
		{
			Entity e = (Entity) iterator.next();
			if(e.visible)
			{
				boolean draggable = e == hovering && selected == null && e.isMovable(); 
				if(draggable)
				{
					g.translate(-1, -1);
				}
				e.paint(g, projector);
				if(draggable)
				{
					g.translate(1, 1);
				}
			}
		}
		if(selected != null)
		{
			selected.paint(g, projector);
		}
	}

	public void onBeginDrag(Entity e, Point newpos)
	{
		e.onBeginDrag();
		e.onDrag(newpos);
	}

	public void onEndDrag(Entity e)
	{
		//TODO: move the entity to the front of the list
		e.onEndDrag();
		selected = null;
		Vector toUpdate = (Vector) entities.clone();
		for (Iterator iterator = toUpdate.iterator(); iterator.hasNext();)
		{
			Entity other = (Entity) iterator.next();
			// compute collisions
			// TODO: this is inaccurate
			if(e != other && other.area().contains(e.getHotspot()))
			{
				e.onDropCollision(other);
				other.onDropCollision(e);
			}
		}
	}

	public void onDrag(Entity e, Point newpos)
	{
		e.onDrag(newpos);
		e.pos = (Point) newpos.clone();
	}
}
